#pragma once
#include "sock.hpp"
#include "log.hpp"
#include <functional>
#include <poll.h>
#include <sys/epoll.h>
using namespace std;

uint16_t default_port = 8080;
static const int default_fd = -1; // 默认文件描述符
static const int default_num = 64;
static const int default_value = -1; // 用来设置默认的epfd;
static const int size = 128;         // 这里是epoll模型中的事件个数
using func_t = function<string(const string &)>;

class EpollServer
{
public:
    EpollServer(func_t func, uint16_t port = default_port, int num = default_num)
        : _listensock(-1), _port(port), _num(num), _func(func), _epfd(default_value), _revs(nullptr)
    {
    }
    void initServer()
    {
        // 创建套接字
        _listensock = sock::Socket();
        // 绑定
        sock::Bind(_listensock, _port);
        // 监听
        sock::Listen(_listensock);
        // 创建epoll模型
        _epfd = epoll_create(size);
        if (_epfd < 0)
        {
            logMessage(FATAL, "create epoll fail! code:%d errstring:%s", errno, strerror(errno));
            exit(EPOLL_CREATE_ERR);
        }
        // 添加listensock到epoll模型中
        struct epoll_event ev;
        ev.data.fd = _listensock;
        ev.events = EPOLLIN;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock, &ev);
        // 设置就绪事件空间
        _revs = new struct epoll_event[_num];
        logMessage(NORMAL, "initServer success");
    }
    void handlerEvent(int readynum)
    {
        logMessage(NORMAL, "handler in");
        // 因为我们是需要遍历所有的fd的，我们并不知道哪个fd已经就绪，所以这里要全部遍历一次
        for (int i = 0; i < readynum; ++i)
        {
            int sock = _revs[i].data.fd;
            uint32_t event = _revs[i].events;
            if (_listensock == sock && (event & EPOLLIN))
            {
                // 处理listensock
                string clientip;
                uint16_t clientport;
                int fd = sock::Accept(_listensock, &clientip, &clientport);
                if (fd < 0)
                    return;
                logMessage(NORMAL, "accept success: [%s:%d]", clientip.c_str(), clientport);
                // 把新的fd放入到revs中
                struct epoll_event ev;
                ev.data.fd = fd;
                ev.events = EPOLLIN;
                epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &ev);
            }
            else if (event & EPOLLIN)
            {
                // 处理读事件
                //这里还是有问题的，你怎么保证你已经读完了?还有发送的时候写端就绪也没有保证，后面写reactor的时候再全部处理
                char buffer[1024];
                ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s] = 0;
                    logMessage(NORMAL, "client say:%s", buffer);
                    string response = _func(buffer);
                    write(sock, response.c_str(), response.size());
                }
                else if (s == 0)
                {
                    // 读完，关闭fd，并删除revs中管理的事件，应该先删除再关闭
                    epoll_ctl(_epfd,EPOLL_CTL_DEL,sock,_revs);
                    close(sock);
                    logMessage(NORMAL, "client quit");
                    return;
                }
                else
                {
                    // 读取错误，关闭fd，并删除revs中管理的事件
                    epoll_ctl(_epfd,EPOLL_CTL_DEL,sock,_revs);
                    close(sock);
                    logMessage(ERROR, "client quit %s", strerror(errno));
                    return;
                }
            }
            else
            {
            } // 暂时只处理读事件，其他的暂时不管
        }
    }
    void start()
    {
        int timeout = -1; // 使用阻塞式等待
        for (;;)
        {
            int n = epoll_wait(_epfd, _revs, _num, timeout);
            switch (n)
            {
            case 0:
            {
                // 这里说明是等待时间到了
                logMessage(NORMAL, "timeout ...");
                break;
            }
            case -1:
            {
                // 这里是出错了,打印错误码
                logMessage(WARNING, "epoll error code:%d,err string:%s", errno, strerror(errno));
                break;
            }
            default:
            {
                // 这里说明已经有至少一个链接就绪了，我们可以进行读取了
                logMessage(NORMAL, "have a event ready!");
                handlerEvent(n); // 这里是有n个事件已经就绪，剩下要处理这n个事件
                break;
            }
            }
        }
    }
    ~EpollServer()
    {
        if (_listensock > 0)
        {
            close(_listensock);
        }
        if (_revs)
        {
            delete _revs;
        }
        if (_epfd != default_value)
        {
            close(_epfd);
        }
    }

private:
    int _listensock; // 监听套接字
    uint16_t _port;  // 端口号
    int _epfd;
    int _num;                  // 就绪队列的大小
    struct epoll_event *_revs; // 放入的事件以及fd
    func_t _func;              // 回调函数
};
